package org.fing.edu.uy.esbadp.action.routing;

import java.util.List;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.fing.edu.uy.esbadp.assertion.AssertProperty;
import org.fing.edu.uy.esbadp.core.AdpFlowConstants;
import org.fing.edu.uy.esbadp.core.AdpFlowInterface;
import org.fing.edu.uy.esbadp.core.AdpFlowRUT;
import org.fing.edu.uy.esbadp.core.AdpFlowTree;
import org.fing.edu.uy.esbadp.core.ExpressionReg;
import org.fing.edu.uy.esbadp.core.entity.GenericTreeNode;
import org.fing.edu.uy.esbadp.logger.LoggerADP;
import org.jboss.internal.soa.esb.assertion.AssertArgument;
import org.jboss.soa.esb.actions.ActionProcessingException;
import org.jboss.soa.esb.actions.annotation.Process;
import org.jboss.soa.esb.message.Body;
import org.jboss.soa.esb.message.Message;

public class RoutingAction {
	
	private static java.util.logging.Logger logger = LoggerADP.getLogger(RoutingAction.class.getSimpleName());
	
	protected ObjectMapperAdaptative _mapper;

	public RoutingAction() {
		_mapper = new ObjectMapperAdaptative();
	}
	
	/**
	 * @param message
	 */
	@Process
	public Message process(Message message)  throws ActionProcessingException {
		
		if(logger.isLoggable(LoggerADP.DEBUG_LEVEL)){
			logger.log(LoggerADP.DEBUG_LEVEL, "Process Action -> {0}", this.getClass().getName());
		}
		
		AssertArgument.isNotNull(message, "message");
		AssertProperty.isNotNull(message, AdpFlowConstants.PropertyFlowServices);
		
		//se obtiene el arbol con las acciones a realizar
		AdpFlowTree adpFlowTree = (AdpFlowTree) message.getProperties().getProperty(AdpFlowConstants.PropertyFlowServices);
		GenericTreeNode<AdpFlowInterface> treeNode = adpFlowTree.getCurrent();
		
		AdpFlowRUT adpFlowAction = (AdpFlowRUT) treeNode.getData();
		int indexNode = 0;
		if(treeNode.getChildren().size() > 0){
			indexNode = getNodeToRouteMessage(message, adpFlowAction, treeNode.getChildren());
		}else{
			throw new ActionProcessingException("Destination AdpFlowRUT does not exist in your configuration.");
		}
		
		GenericTreeNode<AdpFlowInterface> nextCurrent = treeNode.getChildAt(indexNode);
		adpFlowTree.setCurrent(nextCurrent);
		
		return message;
	}

	private int getNodeToRouteMessage(Message message, AdpFlowRUT adpFlowAction, List<GenericTreeNode<AdpFlowInterface>> childrens) {
		if(childrens.size() > 0){
			for(ExpressionReg expression : adpFlowAction.getExpresionReg().keySet()){
				try {
					Object object = _mapper.getObjectFromMessage(message, expression.getPathLocator(), expression.getMatchRegExpression());
					if(object != null){
				        Pattern pattern = Pattern.compile(expression.getMatchRegExpression());
				        Matcher matcher = pattern.matcher(object.toString());
				        if (matcher.find()) {
				        	return adpFlowAction.getExpresionReg().get(expression);
				        }
					}
				} catch (Exception ex) {
					logger.log(Level.SEVERE, "See error:", ex);
				}
			}
			return adpFlowAction.getDefaultIndex();
		}else{
			return 0;
		}
	}
	
	private class ObjectMapperAdaptative{
		
		public Object getObjectFromMessage(Message message, String pathLocator, String expressionMatch) throws Exception{
			Object object=null;
			if("body".equalsIgnoreCase(pathLocator)) {
				object = message.getBody().get(Body.DEFAULT_LOCATION);
	        } else if ("properties".equalsIgnoreCase(pathLocator)) {
	            object = message.getProperties().getProperty(expressionMatch);
	        } else if ("header".equalsIgnoreCase(pathLocator)) {
	            object = message.getHeader().getCall();
	        }else{
	        	throw new Exception(pathLocator + " should start with one of [header,body,properties]");
	        }
			return object;
		}
		
	}

}
